﻿using System;

namespace BaseMod.Models;

public class GlobalGRS
{
    ///// <summary>
    ///// 瓦片分辨率
    ///// </summary>
    ///// <param name="width">瓦片大小</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public double UnitsPerPixel(int width, int zoom)
    //{
    //    return 180.0 / width * 1.0 / Math.Pow(2.0, zoom);
    //}

    ///// <summary>
    ///// 通过列序列号、zoom层级获取xyz(WMTS)服务瓦片左上角经度
    ///// </summary>
    ///// <param name="column">列序列号（x序列）</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public double XYZLongitude(int column, int zoom)
    //{
    //    return (column * 180.0) / Math.Pow(2.0, zoom) - 180.0;
    //}


    ///// <summary>
    ///// 通过行序列号、zoom层级获取xyz(WMTS)服务瓦片左上角纬度
    ///// </summary>
    ///// <param name="row">行序列号（y序列）</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public double XYZLatitude(int row, int zoom)
    //{
    //    double latitude = (row * 180.0) / Math.Pow(2.0, zoom) - 90.0;
    //    return -latitude;
    //}

    ///// <summary>
    ///// 通过坐标经度、zoom层级获取xyz(WMTS)服务瓦片列序列号（x序列）
    ///// </summary>
    ///// <param name="longitude">经度</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns>列序列号（x序列）</returns>
    //public int XYZColumn(double longitude, int zoom)
    //{
    //    return (int)Math.Floor((longitude + 180.0) / 180.0 * Math.Pow(2, zoom));
    //}

    ///// <summary>
    ///// 通过坐标纬度、zoom层级获取xyz(WMTS)服务瓦片行序列号（y序列）
    ///// </summary>
    ///// <param name="latitude">纬度</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns>行序列号（y序列）</returns>
    //public int XYZRow(double latitude, int zoom)
    //{
    //    return (int)Math.Floor((-latitude + 90) / 180.0 * Math.Pow(2, zoom));
    //}

    ///// <summary>
    ///// 获取xyz(WMTS)服务地图层级
    ///// </summary>
    ///// <param name="width">瓦片大小</param>
    ///// <param name="imageWidth">地图视窗宽度</param>
    ///// <param name="maxX">最大经度</param>
    ///// <param name="minX">最小纬度</param>
    ///// <returns></returns>
    //public int XYZZOOM(int width, int imageWidth, double maxX, double minX)
    //{
    //    return (int)Math.Ceiling(Math.Log(180.0 * imageWidth / width / (maxX - minX), 2));
    //}

    ///// <summary>
    ///// 通过坐标经纬度、zoom层级获取xyz(WMTS)服务瓦片行列号（xy）
    ///// </summary>
    ///// <param name="longitude">经度</param>
    ///// <param name="latitude">纬度</param>
    ///// <param name="zoom"></param>
    ///// <returns></returns>
    //public TileIndexInfo XYZTileIndex(double longitude, double latitude, int zoom)
    //{
    //    int column = XYZColumn(longitude, zoom);
    //    int row = XYZRow(latitude, zoom);
    //    return new TileIndexInfo() { Column = column, Row = row };
    //}

    ///// <summary>
    ///// 获取xyz(WMTS)服务瓦片左上角经纬度
    ///// </summary>
    ///// <param name="column">列序号（x序列）</param>
    ///// <param name="row">行序号（y序列）</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public LatLngInfo XYZLatLng(int column, int row, int zoom)
    //{
    //    double lon = XYZLongitude(column, zoom);
    //    double lat = XYZLatitude(row, zoom);
    //    return new LatLngInfo()
    //    {
    //        Lat = lat,
    //        Lng = lon
    //    };
    //}

    ///// <summary>
    ///// 获取TMS服务瓦片左下角经度
    ///// </summary>
    ///// <param name="column">列序号（x序列）</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public double TMSLongitude(int column, int zoom)
    //{
    //    return (column * 180.0) / Math.Pow(2.0, zoom) - 180.0;
    //}

    ///// <summary>
    ///// 获取TMS服务瓦片左下角纬度
    ///// </summary>
    ///// <param name="row">行号（y序列）</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public double TMSLatitude(int row, int zoom)
    //{
    //    double num = (double)(row * 180) / Math.Pow(2.0, (double)zoom) - 90.0;
    //    return num;
    //}

    ///// <summary>
    ///// 获取TMS服务瓦片列号（x序列）
    ///// </summary>
    ///// <param name="longitude">经度</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public int TMSColumn(double longitude, int zoom)
    //{
    //    return (int)Math.Floor((longitude + 180.0) / 180.0 * Math.Pow(2, zoom));
    //}

    ///// <summary>
    ///// 获取TMS服务瓦片行号（y序列）
    ///// </summary>
    ///// <param name="latitude">纬度</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public int TMSRow(double latitude, int zoom)
    //{
    //    return (int)Math.Floor((latitude + 90) / 180.0 * Math.Pow(2, zoom));
    //}


    ///// <summary>
    ///// 获取TMS服务地图层级
    ///// </summary>
    ///// <param name="width">瓦片大小</param>
    ///// <param name="imageWidth">地图视窗宽度</param>
    ///// <param name="maxX">最大经度</param>
    ///// <param name="minX">最小纬度</param>
    ///// <returns></returns>
    //public int TMSZOOM(int width, int imageWidth, double maxX, double minX)
    //{
    //    return (int)Math.Ceiling(Math.Log(180.0 * imageWidth / width / (maxX - minX), 2));
    //}

    ///// <summary>
    ///// 获取TMS服务地图瓦片序列号（xy）
    ///// </summary>
    ///// <param name="longitude">经度</param>
    ///// <param name="latitude">纬度</param>
    ///// <param name="zoom">地图层级</param>
    ///// <returns></returns>
    //public TileIndexInfo TMSTileIndex(double longitude, double latitude, int zoom)
    //{
    //    int column = TMSColumn(longitude, zoom);
    //    int row = TMSRow(latitude, zoom);
    //    return new TileIndexInfo() { Column = column, Row = row };
    //}

    ///// <summary>
    ///// 获取TMS服务瓦片左下角经纬度
    ///// </summary>
    ///// <param name="column"></param>
    ///// <param name="row"></param>
    ///// <param name="zoom"></param>
    ///// <returns></returns>
    //public LatLngInfo TMSLatLng(int column, int row, int zoom)
    //{
    //    double lon = TMSLongitude(column, zoom);
    //    double lat = TMSLatitude(row, zoom);
    //    return new LatLngInfo()
    //    {
    //        Lat = lat,
    //        Lng = lon
    //    };
    //}

    #region 根据经纬度、缩放级别算瓦片号

    /// <summary>
    /// 根据缩放级别和经纬度计算Google规则瓦片号
    /// </summary>
    /// <param name="zoom"></param>
    /// <param name="vlong"></param>
    /// <param name="vlat"></param>
    /// <returns></returns>
    public TileIndexInfo GPStoGoogleTilexy(int zoom, double vlong, double vlat)
    {

        var xlong = this.long2tile(vlong, zoom);

        var ylat = this.lat2tile(vlat, zoom);

        return new TileIndexInfo() { Column = xlong, Row = ylat };

    }

    int long2tile(double lon, int zoom)
    {
        return (int)(Math.Floor((lon + 180) / 360 * Math.Pow(2, zoom)));
    }



    int lat2tile(double lat, int zoom)
    {

        return (int)(Math.Floor((1 - Math.Log(Math.Tan(lat * Math.PI / 180) + 1 / Math.Cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.Pow(2, zoom)));

    }


    /// <summary>
    /// 根据缩放级别和经纬度计算TMS规则瓦片编号
    /// </summary>
    /// <param name="zoom"></param>
    /// <param name="vlong"></param>
    /// <param name="vlat"></param>
    /// <returns></returns>
    public TileIndexInfo GPStoTMSTilexy(int zoom, double vlong, double vlat)
    {
        var tilex = this.long2tileX(vlong, zoom);
        var tiley = this.lat2tileY(vlat, zoom);
        return new TileIndexInfo() { Column = tilex, Row = tiley };
    }
    int long2tileX(double lon, int zoom)
    {
        return (int)(Math.Floor((lon + 180) / 360 * Math.Pow(2, zoom)));
    }

    int lat2tileY(double lat, int zoom)
    {
        return (int)((1 << zoom) - (Math.Floor((1 - Math.Log(Math.Tan(lat * Math.PI / 180) + 1 / Math.Cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.Pow(2, zoom))) - 1);
    }

    #endregion

    #region 根据瓦片号、缩放级别算经纬度
    /**
    * 获取瓦片中心经度
    */
    
    double tile2long(int x,int  z)
    {
        return (x / Math.Pow(2, z) * 360 - 180);
    }
    /**
     * 获取瓦片中心纬度
     * n = 2 ^ zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = arctan(sinh(π * (1 - 2 * ytile / n)))
lat_deg = lat_rad * 180.0 / π

     */
    double tile2lat(int y,int z)
    {
        //var n0 = Math.Pow(2, z);
        //var lat_red = Math.Atan(Math.Sinh(Math.PI * (1 - 2 * y / n0)));
        //var lat_deg = lat_red * 180.0 / Math.PI;
        //return lat_deg;

        var n = Math.PI - 2 * Math.PI * y / Math.Pow(2, z);
        return (180 / Math.PI * Math.Atan(0.5 * (Math.Exp(n) - Math.Exp(-n))));
    }
    /// <summary>
    /// 根据Google规则瓦片号、缩放级别计算经纬度
    /// </summary>
    /// <param name="vzoom">缩放级别</param>
    /// <param name="xlong">瓦片X向编号</param>
    /// <param name="ylat">瓦片Y向编号</param>
    /// <returns></returns>
    public LatLngInfo TileIndexToGoogleLonLat(int vzoom,int xlong,int ylat)
    {
        var _long = this.tile2long(xlong, vzoom);
        var _lat = this.tile2lat(ylat, vzoom);
        return new LatLngInfo() { Lng = _long, Lat = _lat };
    }
    /// <summary>
    /// 根据TMS规则瓦片号、缩放级别计算经纬度
    /// </summary>
    /// <param name="vzoom">缩放级别</param>
    /// <param name="xlong">瓦片X向编号</param>
    /// <param name="ylat">瓦片Y向编号</param>
    /// <returns></returns>
    public LatLngInfo TileIndexToTMSLonLat(int vzoom, int xlong, int ylat)
    {
        var tmsTGoogY = Math.Pow(2, vzoom) - ylat - 1;

        var _long = this.tile2long(xlong, vzoom);
        var _lat = this.tile2lat((int)tmsTGoogY, vzoom);
        return new LatLngInfo() { Lng = _long, Lat = _lat };
    }

    #endregion

}

public class LatLngInfo
{
    public double Lat { get; set; }
    public double Lng { get; set; }
}

public class TileIndexInfo
{
    public int Column { get; set; }
    public int Row { get; set; }

    public override string ToString()
    {
        return string.Format("{0},{1}", Column, Row);
    }
}